package plantshop.model;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Random;

import plantshop.model.plants.NativePlant;
import plantshop.model.plants.Plant;


public class Store implements Serializable{

	private static final long serialVersionUID = 6933181249845323342L;

	private static final Random rand = new Random();
	private static final double DEGEN = 0.8;
	private static final int MAX_DISPLAY_INDEX = 10;

	private int totalScoreOnStreet;

	private List<Display> front;
	private List<Display> back;

	private LinkedList<Customer> customers;
	
	private LinkedList<Creature> creatures;

	private int money;
	private int attractiveness;
	private int sellRating;
	private int sales;

	private Map<Plant, Integer> counts;

	private Location topLeft, bottomRight;

	public Store(int money, double xscale, double yscale){
		this.money = money;
		
		Customer.setExit(new Location((int)(1130*xscale),(int)(450*yscale)));
		Customer.setEntrance(new Location((int)(1130*xscale),(int)(350*yscale)));
		Customer.setXScale(xscale);
		Customer.setYScale(yscale);
		
		attractiveness = 0;
		front = new ArrayList<Display>();
		for(int i=0; i<4;i++)
			front.add(new Display(4, (int)((620+(170*(i%2)))*xscale), (int)((170+(i<2?0:1)*367)*yscale), true, xscale, yscale));
		back = new ArrayList<Display>();
		back.add(new Display(12,(int)(110*xscale),(int)(160*yscale),false, xscale, yscale));
		customers = new LinkedList<Customer>();
		creatures = new LinkedList<Creature>();

		counts = new HashMap<Plant, Integer>();

		totalScoreOnStreet = 0;
		sellRating = 500;
		sales = 0;

		topLeft = new Location((int)(450*xscale),(int)(50*yscale));
		bottomRight = new Location((int)(1280*xscale),(int)(850*yscale));
	}

	public List<Display> getFront() {
		return front;
	}

	public List<Display> getBack() {
		return back;
	}
	
	public Map<Plant,Integer> getCounts(){
		return counts;
	}

	public void buyPlant(int d, int i, Plant p){
		money -= p.getSeedPrice();
		addPlant(false,d,i,p);
	}

	public void createCreature(Creature c){
		if(c!=null)
			creatures.add(c);
	}
	
	public boolean addPlant(boolean where, int d, int i, Plant p){ //true = front
		if(i<0||d<0)//Is this the kind of behavior we want?
			return false;
		if(p==null)
			return false;
		
		Display disp;
		if(where){
			if(d >= front.size() || i >= MAX_DISPLAY_INDEX)
				return false;
			disp = front.get(d);
			if(disp.getPlant(i)==null)
				updateCounts(disp.addPlant(i, p),1);
			else
				return false;
		}
		else{
			if(d >= back.size() || i >= MAX_DISPLAY_INDEX)
				return false;
			disp = back.get(d);
			if(disp.getPlant(i)==null)
				disp.addPlant(i, p);
			else
				return false;
		}

		int rowWidth = (disp.getBottomRight().getX()-disp.getTopLeft().getX())/2;
		int colHeight = (disp.getBottomRight().getY()-disp.getTopLeft().getY())/(disp.size()/2);
		p.setLoc(disp.getTopLeft().getX() + rowWidth*(i%2), disp.getTopLeft().getY() + colHeight*(i/2));
		updateAttractiveness();
		return true;
	}

	public void markPlant(int disp, int loc){
		if(disp < 0 || loc < 0)
			return;
		if(disp >= front.size())
			return;
		if(loc >= MAX_DISPLAY_INDEX)
			return;
		
		Plant p = front.get(disp).getPlant(loc);
		if(p!=null)
			front.get(disp).getPlant(loc).markTaken();
	}

	public void addCustomer(Customer c){
		if(c!=null)
			customers.add(c);
	}

	public void removeCustomer(Customer c){
		customers.remove(c);
	}
	public void removeCustomer(int loc){
		if(loc<customers.size()&&loc>=0)
			customers.remove(loc);
	}
	
	public void removeCreature(Creature c){
		creatures.remove(c);
	}

	@SuppressWarnings("unchecked")
	public List<Customer> getCustomers(){
		return (List<Customer>)customers.clone();
	}

	@SuppressWarnings("unchecked")
	public List<Creature> getCreatures(){
		return (List<Creature>)creatures.clone();
	}
	
	
	public int sellPlant(int disp, int i){
		if(disp < 0 || i < 0)
			return 0;
		if(disp >= front.size())
			return 0;
		if(i >= MAX_DISPLAY_INDEX)
			return 0;
		
		Plant sold = front.get(disp).removePlant(i);
		if(sold!=null){
			money += sold.getSellPrice();
			updateCounts(sold, -1);
			updateAttractiveness();
			if(sold instanceof NativePlant){
				sellRating += sold.getAttractiveness() * 2.5;
			} else{
				sellRating -= sold.getAttractiveness() * 0.5;
			}
			sales++;
			return sold.getSellPrice();
		}
		return 0;
	}


	public int sellRequestedPlant(boolean where, int disp, int i, int dominant, Customer customer)
	{
		if(disp < 0 || i < 0)
			return 0;
		if(i >= MAX_DISPLAY_INDEX)
			return 0;
		
		if(where)
		{
			if(disp >= front.size())
				return 0;
			
			Plant sold = front.get(disp).removePlant(i);
			if(sold != null){
				updateCounts(sold, -1);
				updateAttractiveness();
				if(sold instanceof NativePlant){
					sellRating += sold.getAttractiveness() * 4.0;
					if(sold.getCharacteristics().getDominant() == dominant)
					{
						money += sold.getSellPrice()*4;
						customer.setEmotion(Customer.HAPPY);
					}
					else
						money += sold.getSellPrice()*3;
				} else {
					sellRating -= sold.getAttractiveness();
					customer.setEmotion(Customer.SAD);
					if(sold.getCharacteristics().getDominant() == dominant)
					{
						money += sold.getSellPrice()*2;
					}
					else
						money += sold.getSellPrice()*1;
				}
				sales++;
				return sold.getSeedPrice();
			}
		}
		else
		{
			if(disp >= back.size())
				return 0;
			Plant sold = back.get(disp).removePlant(i);
			if(sold!=null){
				if(sold instanceof NativePlant){
					if(sold.getCharacteristics().getDominant() == dominant)
					{
						money += sold.getSellPrice()*4;
						System.out.println("happy");
						customer.setEmotion(Customer.HAPPY);
					}
					else
						money += sold.getSellPrice()*3;
					sellRating += sold.getAttractiveness() * 2.5;
				} else{
					sellRating -= sold.getAttractiveness();
					customer.setEmotion(Customer.SAD);
					if(sold.getCharacteristics().getDominant() == dominant)
					{
						money += sold.getSellPrice()*2;
					}
					else
						money += sold.getSellPrice()*1;
				}
				sales++;
				return sold.getSellPrice();
			}
		}
		return 0;
		
	}

	public Plant getPlant(boolean where, int disp, int i){ //true = front
		if(disp < 0 || i < 0)
			return null;
		if(i>=MAX_DISPLAY_INDEX)
			return null;
		if(where){
			if(disp>=front.size())
				return null;
			return front.get(disp).getPlant(i);
		}
		else{
			if(disp>=back.size())
				return null;
			return back.get(disp).getPlant(i);
		}
	}

	public void removePlant(boolean where, int disp, int i){ //true = front
		if(where)
			updateCounts(front.get(disp).removePlant(i), -1);
		else
			back.get(disp).removePlant(i);
		updateAttractiveness();
	}

	public int getMoney() {
		return money;
	}

	public int getAttractiveness() {
		return attractiveness + sellRating;
	}

	private void updateCounts(Plant p, int num){
		Plant key = p;
		if(counts.containsKey(key)){
			counts.put(key, counts.get(key)+num);
		}
		else{
			if(num >= 0)
				counts.put(key, num);
		}
	}

	public void updateAttractiveness(){
		int add;
		attractiveness = 0;
		for(Plant key: counts.keySet()){
			int times = counts.get(key);
			add = 0;
			for(int i=0; i<times;i++)
				add += (int)(key.getAttractiveness() * Math.pow(DEGEN,i));
			attractiveness += add;
		}
	}

	public void updateTotalScore(int num){
		totalScoreOnStreet = num;
	}

	public void sendCustomer(){
		if(totalScoreOnStreet > 0 && rand.nextInt(totalScoreOnStreet) < getAttractiveness()){	
			if(CollisionDetector.isValid(Customer.ENTRANCE, null) == 1)
			{
				Customer c = Customer.makeCustomer();
				c.setGoalLoc(findGoalLoc(c));
				addCustomer(c);
				Collections.sort(customers, new CustomerYComparator());
			}

		}
	}
	public void sendCustomer(Customer c){
		if(CollisionDetector.isValid(Customer.ENTRANCE, null) == 1)
		{
			c.setGoalLoc(findGoalLoc(c));
			addCustomer(c);
			Collections.sort(customers, new CustomerYComparator());
		}
	}

	public Location findGoalLoc(Customer c){
		Plant choice = c.getGoalPlant();
		if(choice != null)
		{
			Location goalLoc = null;
			for(Display d : front)
				for(int i = 0; i < d.size(); i++)
				{
					if(d.getPlant(i) == choice)
					{
						
						if(i == 0 || i == 1)
							goalLoc = new Location(d.getTopLeft().getX() + Display.DISPLAY_UNIT_SIZE*i, d.getTopLeft().getY() - Display.DISPLAY_UNIT_SIZE);
						else if(i == d.size() - 2 || i == d.size() - 1)
							goalLoc = new Location(d.getTopLeft().getX() + Display.DISPLAY_UNIT_SIZE*(i%2), d.getBottomRight().getY());
						else if(i % 2 == 0)
							goalLoc = new Location(d.getTopLeft().getX() - Display.DISPLAY_UNIT_SIZE, d.getTopLeft().getY() + Display.DISPLAY_UNIT_SIZE*(i/2));
						else
							goalLoc = new Location(d.getTopLeft().getX() + Display.DISPLAY_UNIT_SIZE, d.getTopLeft().getY() + Display.DISPLAY_UNIT_SIZE*(i/2));
					}
				}

			return goalLoc;
		}
		return null;
	}

	public Plant findGoalPlant(Customer c)
	{
		List<Plant> possiblePlants = new LinkedList<Plant>();
		for(Display d: front){
			for(Plant p: d){
				if(c.wantsToBuy(p)){
					possiblePlants.add(p);
				}
			}
		}
		Plant choice = null;
		if(possiblePlants.size() > 0){
			choice = possiblePlants.get(rand.nextInt(possiblePlants.size()));
		}
		if(choice != null)
			choice.markTaken();
		return choice;
	}

	public Location getPlantAtLoc(int x, int y){
		int disp=0;
		for(Display d: front){
			for(int loc=0; loc < d.size(); loc++){
				if(d.getPlant(loc) != null && d.getPlant(loc).getLoc().getX() == x && d.getPlant(loc).getLoc().getY() == y){
					return new Location(disp, loc);
				}
			}
			disp++;
		}
		return null;
	}

	public Location getPlantAtLoc(boolean where, int x, int y)
	{
		int disp=0;
		if(where)
		{
			for(Display d: front){
				for(int loc=0; loc < d.size(); loc++){
					if(d.getPlant(loc) != null && d.getPlant(loc).getLoc().getX() == x && d.getPlant(loc).getLoc().getY() == y){
						return new Location(disp, loc);
					}
				}
				disp++;
			}
		}
		else
		{
			for(Display d: back){
				for(int loc=0; loc < d.size(); loc++){
					if(d.getPlant(loc) != null && d.getPlant(loc).getLoc().getX() == x && d.getPlant(loc).getLoc().getY() == y){
						return new Location(disp, loc);
					}
				}
				disp++;
			}
		}
		return null;
	}

	public void increaseAttractiveness(int amount)
	{
		sellRating += amount;
	}

	public Location getTopLeft(){
		return topLeft;
	}

	public Location getBottomRight(){
		return bottomRight;
	}

	public int getSales(){
		return sales;
	}

	public void updateSellRating(int num){
		sellRating = num;
	}

	public int getSellRating(){
		return sellRating;
	}

	public void resetSales(){
		sales = 0;
	}
	
	public void setTopLeft(Location topLeft)
	{
		this.topLeft = topLeft;
	}
	
	public void setBottomRight(Location bottomRight)
	{
		this.bottomRight = bottomRight;
	}
}
